package io.gitee.tooleek.lock.spring.boot.core.key.strategy;

import io.gitee.tooleek.lock.spring.boot.annotation.Key;
import io.gitee.tooleek.lock.spring.boot.annotation.Lock;
import io.gitee.tooleek.lock.spring.boot.annotation.RLock;
import io.gitee.tooleek.lock.spring.boot.annotation.ZLock;
import io.gitee.tooleek.lock.spring.boot.core.key.LockKey.Builder;
import io.gitee.tooleek.lock.spring.boot.core.key.RedisLockKey;
import io.gitee.tooleek.lock.spring.boot.core.key.ZookeeperLockKey;
import io.gitee.tooleek.lock.spring.boot.enumeration.InterProcess;
import io.gitee.tooleek.lock.spring.boot.enumeration.LockScheme;
import io.gitee.tooleek.lock.spring.boot.enumeration.LockType;
import io.gitee.tooleek.lock.spring.boot.exception.KeyBuilderException;
import io.gitee.tooleek.lock.spring.boot.exception.LockValidateException;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @BelongsPackage: io.gitee.tooleek.lock.spring.boot.core.strategy
 * @Author: zsx
 * @CreateTime: 2019-04-17 10:00
 * @Description: 方法锁处理
 */
public class MethodKeyStrategy extends KeyStrategy {

    public MethodKeyStrategy(LockScheme scheme, String className, String methodName, Method realMethod, Object[] args) {
        super(scheme, className, methodName, realMethod, args);
    }

    @SuppressWarnings("rawtypes")
    @Override
    public Builder generateBuilder() throws KeyBuilderException {

        Builder keyBuilder;
        if (scheme == LockScheme.REDIS) {
            keyBuilder = RedisLockKey.newRedisKeyBuilder();
        } else {
            keyBuilder = ZookeeperLockKey.newZookeeperKeyBuilder();
        }

        String[] values = realMethod.getAnnotation(Key.class).value();

        //增加keys，用来保存所有的key，这样子让可重用锁支持多key
        List<String> keys = new ArrayList<>();

        for (int i = 0; i < args.length; i++) {
            Object obj = args[i];
            Class objectClass = obj.getClass();
            Field[] fields = objectClass.getDeclaredFields();
            for (String value : values) {
                String[] propertyName = value.split("\\.");
                String[] className = objectClass.getName().split("\\.");
                if (propertyName[0].equals(className[className.length - 1])) {
                    for (Field field : fields) {
                        field.setAccessible(true);
                        if (field.getName().equals(propertyName[1])) {
                            try {
                                keys.add(field.getName() + ":" + field.get(obj).toString());
                            } catch (IllegalAccessException e) {
                                throw new KeyBuilderException("生成builder失败", e);
                            }
                        }
                    }
                }
            }
        }

        //对keys排序
        Collections.sort(keys);

        if (realMethod.getAnnotation(Lock.class) != null) {
            Lock lock = realMethod.getAnnotation(Lock.class);
            dealWithKeyBuilder(lock.lockType(), keyBuilder, keys);
        } else if (realMethod.getAnnotation(RLock.class) != null) {
            RLock lock = realMethod.getAnnotation(RLock.class);
            dealWithKeyBuilder(lock.lockType(), keyBuilder, keys);
        } else if (realMethod.getAnnotation(ZLock.class) != null) {
            //这里我没啥用zookpeeper做过锁，所以，我只能推测zk锁的mutex等于REENTRANT锁
            ZLock lock = realMethod.getAnnotation(ZLock.class);
            if (lock.interProcess() == InterProcess.MUTEX) {
                dealWithKeyBuilder(LockType.REENTRANT, keyBuilder, keys);
            } else {
                dealWithKeyBuilder(null, keyBuilder, keys);
            }
        } else {
            throw new LockValidateException("unsupport lock type");
        }

        return keyBuilder;
    }
}

